<template>
  <div class="explore-node-page">
    <!-- 顶部返回与路径 -->
    <div class="page-topbar">
      <el-button class="back-btn" round size="small" @click="$router.push('/explore')">
        <el-icon><ArrowLeft /></el-icon>
        返回探索
      </el-button>
      <div class="crumb">探索 · 节点详情</div>
    </div>
    <div v-if="node" class="node-head explore-themed">
      <h2>{{ node.title }}</h2>
      <p class="desc">{{ node.short_desc }}</p>
      <div class="meta">
        <el-tag size="small">Lv. {{ node.level }}</el-tag>
        <el-tag size="small" type="info" v-if="node.category">{{ node.category }}</el-tag>
        <el-tag v-if="node.is_locked" size="small" type="warning">未解锁</el-tag>
      </div>
      <el-alert v-if="node.is_locked" type="warning" show-icon :closable="false" class="mb-8"
        :title="node.locked_reason || '该节点暂未解锁，完成前置条件后再来尝试'" />
    </div>

    <el-row :gutter="16" class="content-row">
      <el-col :md="14" :xs="24">
        <el-card class="lore-card explore-themed" v-loading="loading.lore">
          <template #header>
            <div class="card-header">
              <span class="card-title">
                <el-icon class="mr-6"><Reading /></el-icon>
                解读 · Lore
              </span>
              <div class="actions">
                <el-button size="small" text @click="loreCollapsed = !loreCollapsed">
                  <el-icon class="mr-4"><Collection /></el-icon>
                  {{ loreCollapsed ? '展开' : '收起' }}
                </el-button>
                <el-button size="small" @click="refreshLore(false)" :loading="loading.lore">
                  <el-icon class="mr-4"><Refresh /></el-icon>
                  刷新
                </el-button>
                <el-button size="small" type="warning" @click="refreshLore(true)" :loading="loading.lore">
                  <el-icon class="mr-4"><RefreshRight /></el-icon>
                  强制重生
                </el-button>
              </div>
            </div>
          </template>
          <el-alert v-if="!loreCollapsed" type="info" show-icon :closable="false" class="mb-8"
            title="提示：刷新会复用缓存（若存在），强制重生会重新生成并覆盖缓存。" />
          <template v-if="!loreCollapsed">
            <div v-if="lore">
              <div class="lore-content" v-html="renderLore(lore)"></div>
            </div>
            <el-empty v-else description="暂无解读" />
          </template>
          <template v-else>
            <el-alert type="info" :closable="false" show-icon title="已收起解读"
              description="练习时可专注任务，随时点击右上角“展开”查看解读。" />
          </template>
        </el-card>
      </el-col>
      <el-col :md="10" :xs="24">
        <el-card class="quest-card explore-themed" v-loading="loading.quests">
          <template #header>
            <div class="card-header">
              <span class="card-title">
                <el-icon class="mr-6"><Collection /></el-icon>
                任务
              </span>
              <div class="actions">
                <el-button size="small" @click="rulesVisible = true">规则说明</el-button>
              </div>
            </div>
          </template>
          <el-empty v-if="quests.length===0" description="暂无任务" />
          <div v-else class="quest-list">
            <div v-for="q in quests" :key="q.id" class="quest-item" :class="['type-'+q.type.toLowerCase(), q.status==='DONE'?'is-done':'']">
              <!-- 左侧类型图标 -->
              <div class="qi-left">
                <span class="icon-badge" :class="'t-'+q.type.toLowerCase()">
                  <el-icon v-if="q.type==='READ'"><Reading /></el-icon>
                  <el-icon v-else-if="q.type==='VISIT'"><CircleCheck /></el-icon>
                  <el-icon v-else-if="q.type==='COLLECT'"><Box /></el-icon>
                  <el-icon v-else><List /></el-icon>
                </span>
              </div>
              <!-- 中部信息区 -->
              <div class="qi-main">
                <div class="q-title">
                  {{ q.title }}
                  <el-tag v-if="q.status==='DONE'" type="success" size="small" class="ml-6">已完成</el-tag>
                </div>
                <div class="q-require">
                  <template v-if="q.type==='READ'">
                    阅读计时 · 至少 {{ q.requirement_json?.min_read_seconds || 0 }} 秒
                  </template>
                  <template v-else-if="q.type==='VISIT'">
                    已阅确认 · {{ q.requirement_json?.target || '点击「已阅确认」后可完成' }}
                  </template>
                  <template v-else-if="q.type==='COLLECT'">
                    收集目标 · 需要 {{ q.requirement_json?.count || 0 }} 次
                  </template>
                  <template v-else-if="q.type==='QUIZ_SELECT'">
                    选择题 · {{ q.requirement_json?.mode==='multi' ? '多选' : '单选' }}
                  </template>
                  <template v-else-if="q.type==='MATCH'">
                    配对 · 将左侧名词与右侧释义两两对应，全部正确即可完成
                  </template>
                  <template v-else-if="q.type==='ORDER'">
                    排序 · 通过上移/下移调整卡片，恢复正确顺序
                  </template>
                  <template v-else>类型：{{ q.type }}</template>
                </div>
                <!-- 小游戏组件区 -->
                <div v-if="q.type==='MATCH' && q.status!=='DONE'" class="mini-wrap">
                  <MatchGame :quest="q" @progress="onMiniProgress(q, $event)" @canFinishChanged="(v)=>setMiniCanFinish(q,v)" />
                  <div class="mini-caption">完成全部配对后，点击“完成”提交。</div>
                </div>
                <div v-if="q.type==='ORDER' && q.status!=='DONE'" class="mini-wrap">
                  <OrderGame :quest="q" @progress="onMiniProgress(q, $event)" @canFinishChanged="(v)=>setMiniCanFinish(q,v)" />
                  <div class="mini-caption">当顺序与参考答案一致时，点击“完成”提交。</div>
                </div>
                <!-- COLLECT 进度条 -->
                <div v-if="q.type==='COLLECT'" class="collect-line">
                  <el-progress :stroke-width="10" :percentage="Math.min(100, Math.round(100*((q.progress_json?.count||0)/(q.requirement_json?.count||1))))" status="success" :text-inside="true" />
                  <div class="collect-text">{{ (q.progress_json?.count || 0) }} / {{ (q.requirement_json?.count || 0) }}</div>
                </div>
                <!-- QUIZ 展示问题 -->
                <div v-if="q.type==='QUIZ_SELECT'" class="quiz-question">{{ q.requirement_json?.question || '答题' }}</div>
                <!-- QUIZ 选项（按钮风格） -->
                <div v-if="q.type==='QUIZ_SELECT'" class="quiz-options">
                  <template v-if="(q.requirement_json?.mode||'single')==='single'">
                    <el-radio-group v-model="quizSingle[q.id]" @change="onQuizChange(q)" size="small">
                      <el-radio-button v-for="opt in (q.requirement_json?.options||[])" :key="opt.id" :label="String(opt.id)">
                        {{ opt.id }}. {{ opt.text }}
                      </el-radio-button>
                    </el-radio-group>
                  </template>
                  <template v-else>
                    <el-checkbox-group v-model="quizMulti[q.id]" @change="onQuizChange(q)" size="small">
                      <el-checkbox-button v-for="opt in (q.requirement_json?.options||[])" :key="opt.id" :label="String(opt.id)">
                        {{ opt.id }}. {{ opt.text }}
                      </el-checkbox-button>
                    </el-checkbox-group>
                  </template>
                </div>
              </div>
              <!-- 右侧动作区 -->
              <div class="qi-actions">
                <QuestTimer
                  v-if="q.type==='READ' && q.status!=='DONE'"
                  :quest="q"
                  :auto-start="!node?.is_locked"
                  :auto-finish="!node?.is_locked"
                  :throttle-sec="7"
                  @finished="onQuestFinished"
                />
                <template v-else-if="q.type==='VISIT'">
                  <el-button size="small" @click="markVisited(q)" :loading="opId===q.id" :disabled="q.status==='DONE' || node?.is_locked">已阅确认</el-button>
                  <el-button size="small" type="success" @click="finish(q)" :loading="opId===q.id"
                    :disabled="q.status==='DONE' || node?.is_locked || !canFinishVisit(q)">完成</el-button>
                </template>
                <template v-else-if="q.type==='COLLECT'">
                  <el-button size="small" @click="collectOne(q)" :loading="opId===q.id" :disabled="q.status==='DONE' || node?.is_locked">收集 +1</el-button>
                  <el-button size="small" type="success" @click="finish(q)" :loading="opId===q.id"
                    :disabled="q.status==='DONE' || node?.is_locked || !canFinishCollect(q)">完成</el-button>
                </template>
                <template v-else-if="q.type==='QUIZ_SELECT'">
                  <el-button size="small" type="success" @click="finish(q)" :loading="opId===q.id"
                    :disabled="q.status==='DONE' || node?.is_locked || !hasQuizSelection(q)">提交答案</el-button>
                </template>
                <template v-else-if="q.type==='MATCH' || q.type==='ORDER'">
                  <el-button size="small" @click="start(q)" :loading="opId===q.id" :disabled="node?.is_locked">开始</el-button>
                  <el-button size="small" type="success" @click="finish(q)" :loading="opId===q.id"
                    :disabled="q.status==='DONE' || node?.is_locked || !miniCanFinish[q.id]">完成</el-button>
                </template>
                <template v-else>
                  <el-button size="small" @click="start(q)" :loading="opId===q.id" :disabled="node?.is_locked">开始</el-button>
                  <el-button size="small" type="success" @click="finish(q)" :loading="opId===q.id" :disabled="q.status==='DONE' || node?.is_locked">完成</el-button>
                </template>
              </div>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <el-dialog v-model="rulesVisible" title="探索模式 · 节点与任务规则" width="720px">
      <el-scrollbar height="52vh">
        <div class="rules rich">
          <h4>核心概念</h4>
          <ul>
            <li><b>节点</b>：一个知识主题（含简介、AI 解读、任务、解锁条件）。</li>
            <li><b>任务</b>：完成节点目标的微行动，按类型记录进度并给予奖励。</li>
          </ul>

          <el-divider />
          <h4>任务类型与规则</h4>
          <ul class="bullet">
            <li>
              <b>READ</b>（阅读计时）
              <ul>
                <li>要求：requirement_json.min_read_seconds ≥ 1</li>
                <li>进度：progress_json.elapsed 自动累加（前端定时上报，服务端回显）</li>
                <li>完成：elapsed ≥ min_read_seconds 时可自动完成</li>
              </ul>
            </li>
            <li>
              <b>VISIT</b>（已阅确认）
              <ul>
                <li>要求：无/可选 target 文本</li>
                <li>进度：progress_json.visited = true</li>
                <li>完成：visited === true</li>
              </ul>
            </li>
            <li>
              <b>COLLECT</b>（收集计数）
              <ul>
                <li>要求：requirement_json.count ≥ 1</li>
                <li>进度：progress_json.count 逐次 +1</li>
                <li>完成：count ≥ requirement_json.count</li>
              </ul>
            </li>
            <li>
              <b>QUIZ_SELECT</b>（选择题）
              <ul>
                <li>模式：single 或 multi</li>
                <li>进度：progress_json.selected = ['A','C', ...]</li>
                <li>完成：所选集合与答案集合严格相等</li>
              </ul>
            </li>
            <li>
              <b>MATCH</b>（配对）
              <ul>
                <li>玩法：把概念与释义两两配对。</li>
                <li>完成：所有配对均正确即可完成。</li>
                <li>小贴士：不确定时可以尝试多次，错误不会扣分，但会影响用时记录。</li>
              </ul>
            </li>
            <li>
              <b>ORDER</b>（排序）
              <ul>
                <li>玩法：通过“上移/下移”调整卡片位置，恢复正确的步骤顺序。</li>
                <li>完成：当前顺序与参考答案完全一致即可完成。</li>
                <li>小贴士：先确定首尾，再微调中间位置更高效。</li>
              </ul>
            </li>
          </ul>

          <el-divider />
          <h4>解锁与等级</h4>
          <ul>
            <li>图谱前置：NodeEdge(relation_type='unlock') 定义解锁前置，多个前置为 AND。</li>
            <li>等级门槛：Lv 是硬门槛，未达等级不可开始/完成任务。</li>
            <li>兼容历史：若历史上存在多个 READ，仅需完成其一即视为满足该节点的 READ 前置。</li>
          </ul>

          <el-divider />
          <h4>进度、成就与安全</h4>
          <ul>
            <li>进度回显：所有上报会回写 progress_json，刷新页面不丢失。</li>
            <li>成就授予：在任务完成或进入成就页时判定授予。</li>
            <li>权限校验：写操作（开始/进度/完成）需登录；未解锁时按钮禁用，服务端亦强校验。</li>
          </ul>

          <el-divider />
          <h4>AI 解读（Lore）</h4>
          <ul>
            <li>缓存策略：优先读取缓存；“强制重生”将重新生成并覆盖缓存。</li>
            <li>内容格式：markdown 渲染，支持基础排版与列表。</li>
          </ul>

          <el-alert type="warning" :closable="false" show-icon class="mt-8"
            title="小贴士：" description="建议优先完成 READ 任务以解锁后续节点；多尝试 QUIZ 巩固理解。" />
        </div>
      </el-scrollbar>
      <template #footer>
        <el-button @click="rulesVisible=false" type="primary">知道了</el-button>
      </template>
    </el-dialog>
  </div>
  <ConfettiBurst ref="confetti">成就达成！</ConfettiBurst>
</template>

<script setup>
import { ref, reactive, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import { exploreApi } from '@/api/explore'
import QuestTimer from '@/components/exploration/QuestTimer.vue'
import MatchGame from '@/components/exploration/MatchGame.vue'
import OrderGame from '@/components/exploration/OrderGame.vue'
import { ElMessage, ElNotification } from 'element-plus'
import ConfettiBurst from '@/components/common/ConfettiBurst.vue'
import { getCachedEarnedIds, computeNewlyEarned, replaceCacheWith } from '@/utils/achievementsCache'
import { renderMarkdown } from '@/utils/markdownRenderer'
import { getToken } from '@/utils/auth'
import { ArrowLeft, Refresh, RefreshRight, Reading, Collection, CircleCheck, List, Box } from '@element-plus/icons-vue'

const route = useRoute()
const nodeId = Number(route.params.id)

const node = ref(null)
const quests = ref([])
const lore = ref(null)
const opId = ref(null)
const loading = reactive({ node: false, quests: false, lore: false })
const rulesVisible = ref(false)
const confetti = ref(null)
const loreCollapsed = ref(false)

const loadNode = async () => {
  loading.node = true
  try { const { data } = await exploreApi.getNode(nodeId); node.value = data } finally { loading.node = false }
}
const loadQuests = async () => {
  loading.quests = true
  try {
    const { data } = await exploreApi.getNodeQuests(nodeId)
    quests.value = data
    // 初始化 QUIZ 选择状态（从 progress_json.selected 回显）
    data.forEach((q) => {
      if (q.type === 'QUIZ_SELECT') {
        const sel = (q.progress_json && Array.isArray(q.progress_json.selected)) ? q.progress_json.selected.map(String) : []
        if ((q.requirement_json?.mode || 'single') === 'single') {
          quizSingle[q.id] = sel[0] || ''
        } else {
          quizMulti[q.id] = sel
        }
      }
    })
  } finally { loading.quests = false }
}
const loadLore = async () => {
  loading.lore = true
  try { const { data } = await exploreApi.getLore(nodeId); lore.value = data } finally { loading.lore = false }
}

const refreshLore = async (force) => {
  loading.lore = true
  try {
    const { data } = await exploreApi.refreshLore(nodeId, force)
    lore.value = data
    ElMessage.success(force ? '已强制重生 Lore' : '已刷新 Lore')
  } catch (e) {
    ElMessage.error(e?.response?.data?.detail || '刷新失败，请稍后重试')
  } finally { loading.lore = false }
}

const start = async (q) => {
  if (!getToken()) { ElMessage.warning('请先登录'); return }
  opId.value = q.id
  try { await exploreApi.startQuest(q.id); await loadQuests() } finally { opId.value = null }
}
const finish = async (q) => {
  if (!getToken()) { ElMessage.warning('请先登录'); return }
  opId.value = q.id
  try {
    await exploreApi.finishQuest(q.id, {})
    await loadQuests()
    // 完成任务后提示成就：请求一次成就列表，找出 earned 的项弹窗提示
    try {
      const res = await exploreApi.achievements()
      const list = Array.isArray(res.data) ? res.data : []
      const earned = list.filter(a => a.earned && a.earned_at)
      const currentIds = new Set(earned.map(a => String(a.id)))
      const newlyIds = computeNewlyEarned(currentIds)
      if (newlyIds.length) {
        const nameMap = new Map(earned.map(a => [String(a.id), a.title]))
        const names = newlyIds.map(id => nameMap.get(id)).filter(Boolean)
        const msg = names.length ? `新授予：${names.join('、')}。前往「成就徽章」查看全部。` : `有新成就已授予，前往「成就徽章」查看。`
        ElNotification({ title: '成就达成', message: msg, type: 'success', duration: 4200 })
        // 播放礼花动画
        try { confetti.value?.show(1600) } catch {}
      }
      // 更新本地缓存为最新
      replaceCacheWith(currentIds)
    } catch {}
  } catch (e) {
    ElMessage.error(e?.response?.data?.detail || '完成失败')
  } finally { opId.value = null }
}

const onQuestFinished = async () => {
  await loadQuests()
}

onMounted(() => { loadNode(); loadQuests(); loadLore() })
// 根据节点类别默认收起 Lore（比如“练习”类）
watch(() => node.value, (nv) => {
  if (!nv) return
  const cat = (nv.category || '').trim()
  loreCollapsed.value = (cat === '练习')
}, { immediate: true })

// Helpers for VISIT & COLLECT types
const ensureStarted = async (q) => {
  // 若未进入进行中，则先开始
  if (q.status !== 'ACTIVE') {
    await exploreApi.startQuest(q.id)
  }
}

const canFinishVisit = (q) => {
  return !!(q?.progress_json?.visited)
}

const markVisited = async (q) => {
  if (!getToken()) { ElMessage.warning('请先登录'); return }
  if (q.status === 'DONE') return
  opId.value = q.id
  try {
    await ensureStarted(q)
    const target = q?.requirement_json?.target
    await exploreApi.reportProgress(q.id, { payload: { visited: true, ...(target ? { target } : {}) } })
    await loadQuests()
    ElMessage.success('已记录已阅')
  } catch (e) {
    ElMessage.error(e?.response?.data?.detail || '操作失败')
  } finally { opId.value = null }
}

const canFinishCollect = (q) => {
  const cur = Number(q?.progress_json?.count || 0)
  const need = Number(q?.requirement_json?.count || 0)
  return cur >= need && need > 0
}

const collectOne = async (q) => {
  if (!getToken()) { ElMessage.warning('请先登录'); return }
  if (q.status === 'DONE') return
  opId.value = q.id
  try {
    await ensureStarted(q)
    const next = Number(q?.progress_json?.count || 0) + 1
    await exploreApi.reportProgress(q.id, { payload: { count: next } })
    await loadQuests()
    const need = Number(q?.requirement_json?.count || 0)
    if (next >= need && need > 0) {
      ElMessage.success(`已收集 ${next}/${need}，可完成`)
    } else {
      ElMessage.success(`已收集 ${next}/${need || '-'}`)
    }
  } catch (e) {
    ElMessage.error(e?.response?.data?.detail || '操作失败')
  } finally { opId.value = null }
}

// QUIZ helpers
const quizSingle = reactive({}) // { [questId]: 'A' }
const quizMulti = reactive({})  // { [questId]: ['A','C'] }

const hasQuizSelection = (q) => {
  const mode = (q.requirement_json?.mode || 'single')
  if (mode === 'single') return !!quizSingle[q.id]
  const arr = quizMulti[q.id] || []
  return Array.isArray(arr) && arr.length > 0
}

const onQuizChange = async (q) => {
  if (!getToken()) return
  try {
    await ensureStarted(q)
    const mode = (q.requirement_json?.mode || 'single')
    const selected = mode === 'single' ? (quizSingle[q.id] ? [quizSingle[q.id]] : []) : (quizMulti[q.id] || [])
    await exploreApi.reportProgress(q.id, { payload: { selected } })
  } catch (e) {
    // 静默失败即可，避免打断交互；完成时会再次校验
  }
}

// Mini-game (MATCH/ORDER) helpers
const miniCanFinish = reactive({}) // { [questId]: boolean }
function setMiniCanFinish(q, v){ miniCanFinish[q.id] = !!v }
async function onMiniProgress(q, payload){
  if (!getToken()) return
  try {
    await ensureStarted(q)
    await exploreApi.reportProgress(q.id, { payload })
  } catch (e) { /* ignore */ }
}

function renderLore(l) {
  // 后端返回 { node_id, cached, content_md }
  const md = l?.content_md || ''
  try { return renderMarkdown(md) } catch { return (md || '').replace(/\n/g,'<br/>') }
}
</script>

<style scoped>
.explore-node-page { display:block; }
.content-row { margin-top: 8px; }
.page-topbar { display:flex; align-items:center; justify-content:space-between; margin: 4px 0 8px; }
.back-btn { background: rgba(255,255,255,.6); border-color: rgba(0,0,0,.06); }
.crumb { color:#666; font-size:12px; }

.node-head { margin: 10px 0 16px; }
.node-head h2 { margin:0; font-size:22px; letter-spacing:.2px; }
.node-head .desc { color:#666; margin:6px 0 8px; line-height:1.6; }
.node-head .meta { display:flex; gap:8px; }

.card-header { display:flex; align-items:center; justify-content:space-between; }
.card-title { display:flex; align-items:center; font-weight:600; }
.mr-4 { margin-right:4px; }
.mr-6 { margin-right:6px; }
.ml-6 { margin-left:6px; }
.mb-8 { margin-bottom:8px; }
.mt-8 { margin-top:8px; }

.lore-content { white-space:normal; color:#333; line-height:1.8; }

/* 任务列表布局 */
.quest-list { display:flex; flex-direction:column; gap:10px; }
.quest-item { display:grid; grid-template-columns: 42px 1fr auto; gap:10px; align-items:center; border:1px solid rgba(0,0,0,.06); border-radius:10px; padding:12px; background: rgba(255,255,255,.6); }
.quest-item.is-done { opacity:.85; }
.qi-left { display:flex; align-items:center; justify-content:center; }
.icon-badge { width:28px; height:28px; border-radius:50%; display:flex; align-items:center; justify-content:center; background: #f0f3f6; color:#4e5969; }
.icon-badge.t-read { background:#fff5e6; color:#c97a00; }
.icon-badge.t-visit { background:#ecfff2; color:#17b26a; }
.icon-badge.t-collect { background:#eef4ff; color:#2e5aac; }
.icon-badge.t-quiz_select { background:#f5e8ff; color:#7a3ea1; }

.qi-main { min-width:0; }
.q-title { font-weight:600; line-height:1.5; }
.q-require { color:#666; font-size:13px; margin-top:2px; }
.collect-line { margin-top:6px; }
.collect-text { color:#666; font-size:12px; margin-top:2px; }
.mini-wrap { margin-top:8px; }

.qi-actions { display:flex; gap:8px; align-items:center; }

/* Quiz 样式 */
.quiz-question { margin-top:8px; font-weight:500; }
.quiz-options { margin-top:6px; display:flex; flex-wrap:wrap; gap:6px; }

.collect-progress { color:#666; font-size:13px; align-self:center; }
</style>
